#include <iostream>
#include <GL/glut.h>    // Header File For The GLUT Library 
#include <GL/gl.h>	// Header File For The OpenGL32 Library
#include <GL/glu.h>	// Header File For The GLu32 Library
#include <unistd.h>     // Header File For sleeping.
#include "../libfractal/dragon.hpp"
#include "../libfractal/fvector.hpp"


using namespace std;


/* ASCII code for the escape key. */
#define ESCAPE 27
#define HALF_WIDTH 10.0f
#define HALF_HEIGHT 10.0f


/* The number of our GLUT window */
int window;
int cur_wnd_width, cur_wnd_height;
FVector2d l(-0.5, 0.0), r(0.5, 0.0);
Dragon s;
FVector2d cur_pos;
GLfloat zoom_factor = 1.0f;
FVertexArray va;


void render(void)
{
    cout << "render age: "<< s.age << endl;
    va.reserve(s.vertex_num());
    cout << "vnum: " << va.capacity() << endl;;
    s.render(va, s.age, l, r);
    cout << "rendered" << endl;
}


void reset_state(void)
{
    cur_pos.x = cur_pos.y = 0.0;
    zoom_factor = 1.0f;
    
    s.age = 0;

    render();
}


void set_projection_matrix()
{
    float rate;

    if (cur_wnd_height == 0)
	cur_wnd_height = 1;
    if (cur_wnd_width == 0)
	cur_wnd_width = 1;

    if (cur_wnd_width < cur_wnd_height)
	rate = (float)cur_wnd_height / cur_wnd_width;
    else
	rate = (float)cur_wnd_width / cur_wnd_height;
    rate *= zoom_factor;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (cur_wnd_width < cur_wnd_height)
	gluOrtho2D(-HALF_WIDTH*zoom_factor, HALF_WIDTH*zoom_factor, 
		   -HALF_HEIGHT*rate, HALF_HEIGHT*rate);
    else
	gluOrtho2D(-HALF_WIDTH*rate, HALF_WIDTH*rate,
		   -HALF_HEIGHT*zoom_factor, HALF_HEIGHT*zoom_factor);

    glMatrixMode(GL_MODELVIEW);
}


void init_gl(int w, int h)
// We call this right after our OpenGL window is created.
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClearDepth(1.0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_SMOOTH);

    cur_wnd_width = w; cur_wnd_height = h;
    set_projection_matrix();

    reset_state();
}


void resize_gl_scene(int w, int h)
{
    glViewport(0, 0, w, h);
    cur_wnd_width = w; cur_wnd_height = h;
    set_projection_matrix();
}


/* The main drawing function. */
void draw_gl_scene()
{
    // Clear The Screen And The Depth Buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();  // Reset The View

    glTranslatef(cur_pos.x, cur_pos.y, 0.0f);
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(2, GL_DOUBLE, 0, va.c_ptr());
    glDrawArrays(GL_LINES, 0, va.size());
    glDisableClientState(GL_VERTEX_ARRAY);

    // swap buffers to display, since we're double buffered.
    glutSwapBuffers();
}


/* The function called whenever a key is pressed. */
void key_pressed(unsigned char key, int x, int y) 
{
    /* avoid thrashing this procedure */
    usleep(100);

    /* If escape is pressed, kill everything. */
    switch (key) 
    { 
    case ESCAPE:
        /* shut down our window */
    	glutDestroyWindow(window);
    	/* vertice the program...normal termination. */
    	exit(0);
    	break;
    case 'w':
	cur_pos.y += 0.1;
    	break;
    case 's':
    	cur_pos.y -= 0.1;
    	break;
    case 'a':
	cur_pos.x -= 0.1;
	break;
    case 'd':
	cur_pos.x += 0.1;
	break;
    case 't':
	zoom_factor += 0.1;
	set_projection_matrix();
	break;
    case 'g':
	if (zoom_factor > 0.1)
	    zoom_factor -= 0.1;
	set_projection_matrix();
	break;
    case 'u':
	s.age++;
	render();
	break;
    case 'j':
	if (s.age)
	    s.age--;
	render();
	break;
    case 'r':
	reset_state();
    	break;
    }
}


int main(int argc, char **argv) 
{  
    glutInit(&argc, argv);  

    /* Select type of Display mode:   
       Double buffer 
       RGBA color
       Alpha components supported 
       Depth buffer */  
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);  

    /* get a 640 x 480 window */
    glutInitWindowSize(640, 480);  

    /* the window starts at the upper left corner of the screen */
    glutInitWindowPosition(0, 0);  

    /* Open a window */  
    window = glutCreateWindow("lsystem");  

    /* Register the function to do all our OpenGL drawing. */
    glutDisplayFunc(&draw_gl_scene);  

    /* Go fullscreen.  This is the soonest we could possibly go fullscreen. */
    //glutFullScreen();

    /* Even if there are no events, redraw our gl scene. */
    glutIdleFunc(&draw_gl_scene);

    /* Register the function called when our window is resized. */
    glutReshapeFunc(&resize_gl_scene);

    /* Register the function called when the keyboard is pressed. */
    glutKeyboardFunc(&key_pressed);

    /* Initialize our window. */
    init_gl(640, 480);
  
    /* Start Event Processing Engine */  
    glutMainLoop();  

    return 1;
}
